package org.interesting.joone;

import org.joone.engine.FullSynapse;
import org.joone.engine.LinearLayer;
import org.joone.engine.Monitor;
import org.joone.engine.SigmoidLayer;
import org.joone.engine.learning.TeachingSynapse;
import org.joone.io.FileOutputSynapse;
import org.joone.io.MemoryInputSynapse;
import org.joone.io.MemoryOutputSynapse;
import org.joone.net.NeuralNet;

/**
 * NN示例
 * 
 * @author wwxiang
 * @since 2018/6/25.
 */
public class XorSample {

	/**
	 * NeuralNet相关组件
	 */
	private NeuralNet nnet;
	/**
	 * 网络的输入输出
	 */
	private MemoryInputSynapse inputSynapse;
	private MemoryInputSynapse desiredOutputSynapse;
	private MemoryOutputSynapse outputSynapse;
	/**
	 * 3 层
	 */
	private LinearLayer input;
	private SigmoidLayer hidden;
	private SigmoidLayer output;
	/**
	 * 是否单线程运行整个网络
	 */
	boolean singleThreadMode = true;

    /**
     * 学习用数据集
     */
	private double[][] data = new double[][] {
	        { 0.0, 0.0, 0.0 },
            { 0.0, 1.0, 1.0 },
            { 1.0, 0.0, 1.0 },
			{ 1.0, 1.0, 0.0 }
	};

	public static void main(String[] args) {
		XorSample xor = new XorSample();
		xor.initNeuralNet();
		xor.train();
		xor.interrogate();
	}

	/**
	 * 初始化网络
	 */
	protected void initNeuralNet() {
		// 创建标准的3层结构
		input = new LinearLayer();
		hidden = new SigmoidLayer();
		output = new SigmoidLayer();

		// set the dimensions of the layers
		input.setRows(2);
		hidden.setRows(3);
		output.setRows(1);

		input.setLayerName("L.input");
		hidden.setLayerName("L.hidden");
		output.setLayerName("L.output");

		// 给每层之间添加连接Synapse
		// input -> hidden conn.
		FullSynapse input2Hidden = new FullSynapse();
		// 用此synapse将2层联系起来
		input.addOutputSynapse(input2Hidden);
		hidden.addInputSynapse(input2Hidden);

		// hidden -> output conn.
		FullSynapse hidden2output = new FullSynapse();
		hidden.addOutputSynapse(hidden2output);
		output.addInputSynapse(hidden2output);

		// 给输入层添加输入synapse
		inputSynapse = new MemoryInputSynapse();
		input.addInputSynapse(inputSynapse);

		// 给Trainer添加期望输出synapse
		desiredOutputSynapse = new MemoryInputSynapse();
		TeachingSynapse trainer = new TeachingSynapse();
		trainer.setDesired(desiredOutputSynapse);

		// Now we add this structure to a NeuralNet object
		nnet = new NeuralNet();
		nnet.addLayer(input, NeuralNet.INPUT_LAYER);
		nnet.addLayer(hidden, NeuralNet.HIDDEN_LAYER);
		nnet.addLayer(output, NeuralNet.OUTPUT_LAYER);
		nnet.setTeacher(trainer);
		output.addOutputSynapse(trainer);
		nnet.addNeuralNetListener(new DefaultNNListener());
	}

	public void train() {
		// set the inputs
		inputSynapse.setInputArray(data);
		inputSynapse.setAdvancedColumnSelector("1,2");
		// set the desired outputs
		desiredOutputSynapse.setInputArray(data);
		desiredOutputSynapse.setAdvancedColumnSelector("3");

		// get the monitor object to train or feed forward
		Monitor monitor = nnet.getMonitor();
		monitor.setLearningRate(0.8);
		monitor.setMomentum(0.3);
		monitor.setTrainingPatterns(data.length);
		monitor.setTotCicles(1000);
		monitor.setLearning(true);

		long initMs = System.currentTimeMillis();
		// Run the network in single-thread, synchronized mode
		nnet.getMonitor().setSingleThreadMode(singleThreadMode);
		nnet.go(true);
		System.out.println("Training elapse time= " + (System.currentTimeMillis() - initMs) + " ms");
	}

	/**
	 * 对输入数据进行预测
	 */
	private void interrogate() {
		// set the inputs
		inputSynapse.setInputArray(data);
		inputSynapse.setAdvancedColumnSelector("1,2");
		Monitor monitor = nnet.getMonitor();
		monitor.setTrainingPatterns(4);
		monitor.setTotCicles(1);
		monitor.setLearning(false);
        ConsoleOutputSynapse console = new ConsoleOutputSynapse();
		if (nnet != null) {
			nnet.addOutputSynapse(console);
//			System.out.println(nnet.check());
			nnet.getMonitor().setSingleThreadMode(singleThreadMode);
			nnet.go();
		}
	}

}
